<?php

namespace app\common\webdav;

use app\admin\model\StorageLocks;
use app\admin\model\HomeUser;
use app\common\tools\PathTools;
use app\common\webdav\auth\backend\Easy;
use app\common\webdav\auth\backend\Model;
use app\common\webdav\auth\Tools;
use app\common\webdav\browser\Plugin;
use app\common\webdav\collection\Home;
use app\common\webdav\exception\ResponseException;
use app\common\webdav\fs\Directory;
use app\common\webdav\response\WebdavStream;
use app\common\webdav\service\Server;
use app\Request;
use League\Flysystem\Adapter\Local;
use League\Flysystem\Filesystem;
use think\Response;
use Sabre\DAV;
use Sabre\DAV\Auth\Backend\BasicCallBack;
use Sabre\DAV\SimpleCollection;
use think\facade\App;
use think\facade\Log;
use Sabre\DAV\Auth;
use Sabre\DAV\Exception;
use Sabre\DAV\Locks\Backend\PDO;
use Sabre\DAV\Server as DAVServer;
use Sabre\DAV\Tree;
use Sabre\DAV\Version;
use think\facade\Config;
use think\facade\Db;
use think\facade\Session;
use
    Sabre\CalDAV,
    Sabre\DAVACL;
use Sabre\CardDAV\Plugin as CardDAVPlugin;

class IndexWebdav
{

    protected $request;

    protected static $pdo = null;

    protected $loginTools = null;

    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    public function initResponse($server)
    {
        $dav_response = $server->getHttpResponse();

        $dav_headers = $dav_response->getHeaders();

        $tp_headers = [];

        foreach ($dav_headers as $header_key => $header_value) {
            $tp_headers[$header_key] = $header_value[0];
        }

        $dav_code = $dav_response->getStatus();

        $response_body = $dav_response->getBody();

        // $response_body = $dav_response->getBodyAsString();

        return $this->buildResponse($response_body, $dav_code, $tp_headers);
    }

    public function buildResponse($data, $code = 200, $header = [], $type = null)
    {
        // $login_headers = $this->getLoginTools()->getNotAuthenticatedHeader();

        // $header = array_merge($header, $login_headers);

        Log::debug($code);
        Log::debug($header);
        $response_body_type = gettype($data);
        Log::debug('response_body type:' . $response_body_type);


        if ($response_body_type != 'resource' && $response_body_type != 'resource (closed)') {
            Log::debug($data);
        }

        $response = new WebdavStream();

        if (!is_null($data)) {
            $response->data($data);
        }

        $response->code($code);

        $response->header($header);


        if (!is_null($type)) {
            $response->contentType($type);
        }

        return $response;
    }


    public function getIntoCollection($type, $model_user)
    {
        $collection = [];

        if ($type == 'dav') {
            // 进行外部的登录认证
            $collection = new Home($model_user);
        } else {
            $pdo = $this->getPdo();

            $principalBackend = new DAVACL\PrincipalBackend\PDO($pdo);
            $calendarBackend = new CalDAV\Backend\PDO($pdo);
            $collection = new DAV\SimpleCollection('root', [
                new DAV\SimpleCollection('dav'),
                // new CalDAV\CalendarRoot($principalBackend, $calendarBackend)
            ]);
        }

        return $collection;
    }

    public function getLoginTools()
    {
        if (is_null($this->loginTools)) {

            $login_tools = new Tools();

            $this->loginTools = $login_tools;
        }

        return $this->loginTools;
    }

    public function requireLogin(): HomeUser
    {

        $this->getLoginTools();

        return $this->loginTools->requireLogin();
    }

    public function getBaseUri($type)
    {
        $base_url = '/';

        if ($type == 'dav') {
            $base_url = '/dav';
        }

        return $base_url;
    }

    public function run($type = 'dav'): Response
    {
        Log::debug('request url:' . $this->request->url());
        Log::debug('request method:' . $this->request->method());
        Log::debug($this->request->header());
        Log::debug($this->request->server());
        try {

            $server = new Server();

            $model_user = $this->requireLogin();

            $server->tree = new Tree($this->getIntoCollection($type, $model_user));

            $base_url = $this->getBaseUri($type);

            $server->setBaseUri($base_url);

            $pdo = static::getPDO();

            $lockBackend = new PDO($pdo);

            $lockBackend->tableName = StorageLocks::getTable();

            $lockPlugin = new DAV\Locks\Plugin($lockBackend);

            $server->addPlugin($lockPlugin);

            $server->addPlugin(new Plugin());

            $authBackend = new Easy($model_user);

            $authPlugin = new DAV\Auth\Plugin($authBackend);
            $server->addPlugin($authPlugin);

            // // CardDAV plugin
            // $carddavPlugin = new CardDAVPlugin();
            // $server->addPlugin($carddavPlugin);

            // // CalDAV plugin
            // $caldavPlugin = new CalDAV\Plugin();
            // $server->addPlugin($caldavPlugin);

            // ACL plugin
            // $aclPlugin = new DAVACL\Plugin();
            // $server->addPlugin($aclPlugin);

            $server->run();

            return $this->initResponse($server);
        } catch (\Throwable $e) {
            Log::debug($e);
            $DOM = new \DOMDocument('1.0', 'utf-8');
            $DOM->formatOutput = true;

            $error = $DOM->createElementNS('DAV:', 'd:error');
            $error->setAttribute('xmlns:s', DAVServer::NS_SABREDAV);
            $DOM->appendChild($error);

            $h = function ($v) {
                return htmlspecialchars((string) $v, ENT_NOQUOTES, 'UTF-8');
            };

            if (DAVServer::$exposeVersion) {
                $error->appendChild($DOM->createElement('s:sabredav-version', $h(Version::VERSION)));
            }

            $error->appendChild($DOM->createElement('s:exception', $h(get_class($e))));
            $error->appendChild($DOM->createElement('s:message', $h($e->getMessage())));

            if ($e instanceof Exception) {
                $httpCode = $e->getHTTPCode();
                $e->serialize($server, $error);
                $headers = $e->getHTTPHeaders($server);
            } else if ($e instanceof ResponseException) {
                $httpCode = $e->getHttpCode();
                $headers = $e->getHttpHeaders();
            } else {
                $httpCode = 500;
                $headers = [];
            }

            $content = $DOM->saveXML();

            return $this->buildResponse($content, $httpCode, $headers, 'application/xml');
        }
    }

    public static function getPdo()
    {
        if (is_null(static::$pdo)) {
            $dsn = static::parseDsn();
            $username = Config::get('webdav.database.username');
            $password = Config::get('webdav.database.password');
            $params = Config::get('webdav.database.params');
            static::$pdo =  new \PDO($dsn, $username, $password, $params);
        }

        return static::$pdo;
    }

    /**
     * 解析pdo连接的dsn信息
     * @access protected
     * @param  array $config 连接信息
     * @return string
     */
    protected static function parseDsn(): string
    {

        $config = Config::get('webdav.database');

        if (!empty($config['socket'])) {
            $dsn = 'mysql:unix_socket=' . $config['socket'];
        } elseif (!empty($config['hostport'])) {
            $dsn = 'mysql:host=' . $config['hostname'] . ';port=' . $config['hostport'];
        } else {
            $dsn = 'mysql:host=' . $config['hostname'];
        }
        $dsn .= ';dbname=' . $config['database'];

        if (!empty($config['charset'])) {
            $dsn .= ';charset=' . $config['charset'];
        }

        return $dsn;
    }
}
